#include "App.h"
#include "ReportError.h"
#include "Game.h"
#include "Timer.h"

#include "MainGameState.h"

//zero out the globals
App* g_App = 0;

//Handles messages that windows may pass in. Gets events from windows (move mouse, hit key, etc)
LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{
	// All other messages pass to the message handler in the system class.
	if( g_App )
		return g_App->MessageHandler(hwnd, umessage, wparam, lparam);
	else
		return DefWindowProc(hwnd, umessage, wparam, lparam);
}

App::App()
{
	m_hAppInst = 0;			
	m_hMainWnd = 0;

	m_AppPaused = false;
	m_Minimized = false;
	m_Maximized = false;
	m_Resizing = false;

	m_MainWndCaption = "D3D10 Application";

//	m_Graphics = 0;
}

App::App(HINSTANCE hInstance, std::string wndcaption)
{
	// Give the application a name.
	m_hAppInst = hInstance;
	m_hMainWnd = 0;		

	m_AppPaused = false;
	m_Minimized = false;
	m_Maximized = false;
	m_Resizing = false;

	m_MainWndCaption = wndcaption;

	//m_Graphics = 0;
}

App::~App()
{
}

bool App::Initialize()
{
	// Initialize the width and height of the screen to zero before sending the variables into the function.
	int ScreenWidth = 800;
	int ScreenHeight = 600;

	// Initialize the windows api.
	InitWindow(ScreenWidth, ScreenHeight);

	// Create the input object.  This object will be used to handle reading the keyboard input from the user.
	//m_Input = new InputClass;
	//if(!m_Input)
	//{
	//	return false;
	//}

	// Initialize the input object.
	//m_Input->Initialize();

	// Create the graphics object.  This object will handle rendering all the graphics for this application.
	//m_Graphics = new Graphics;
	//if(!m_Graphics)
	//{
	//	return false;
	//}

	// Initialize the graphics object.
	if(!TheGraphics::Instance()->Initialize(ScreenWidth, ScreenHeight, m_hMainWnd))
	{
		return false;
	}

	TheGame::Instance()->SetGameState(TheMainGameState::Instance());

	return true;
}

void App::Shutdown()
{
	// Release the graphics object.
	//if(m_Graphics)
	//{
		TheGraphics::Instance()->Shutdown();
	//	delete m_Graphics;
	//	m_Graphics = 0;
	//}

	// Release the input object.
	//if(m_Input)
	//{
	//	delete m_Input;
	//	m_Input = 0;
	//}

	// Shutdown the window.
	ShutdownWindows();

	return;
}

int App::Run()
{
	MSG msg;
	// Init the message structure
	ZeroMemory(&msg, sizeof(MSG));

	TheTimer::Instance()->Start();

	// Loop until there is a quit message
	while(msg.message != WM_QUIT)
	{
		// Handle the windows messages.
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		else
		{
			if(m_AppPaused)
			{
				Sleep(20);
			}
			else
			{
				TheTimer::Instance()->Update();

				TheGame::Instance()->Run();
				//Frame();
			}
		}
	}

	return (int)msg.wParam;
}

bool App::Frame()
{
	// Do the frame processing for the graphics 
	if(!TheGraphics::Instance()->Frame())
	{
		return false;
	}

	return true;
}

LRESULT CALLBACK App::MessageHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
	switch( umsg )
	{
		// WM_ACTIVATE is sent when the window is activated or deactivated.  
		// We pause the game when the window is deactivated and unpause it 
		// when it becomes active.  
	case WM_ACTIVATE:
		if( LOWORD(wparam) == WA_INACTIVE )
		{
			//m_AppPaused = true;
			//mTimer.stop();
		}
		else
		{
			m_AppPaused = false;
			//mTimer.start();
		}
		return 0;

		// WM_SIZE is sent when the user resizes the window.  
	case WM_SIZE:
		// Save the new client area dimensions.
		TheGraphics::Instance()->GetD3DObj()->SetScreenWidth(LOWORD(lparam));
		TheGraphics::Instance()->GetD3DObj()->SetScreenHeight(HIWORD(lparam));

		if( TheGraphics::Instance()->GetD3DObj()->GetDevice())
		{
			if( wparam == SIZE_MINIMIZED )
			{
				//m_AppPaused = true;
				m_Minimized = true;
				m_Maximized = false;
			}
			else if( wparam == SIZE_MAXIMIZED )
			{
				m_AppPaused = false;
				m_Minimized = false;
				m_Maximized = true;
				TheGraphics::Instance()->GetD3DObj()->OnResize();
			}
			else if( wparam == SIZE_RESTORED )
			{

				// Restoring from minimized state?
				if( m_Minimized )
				{
					m_AppPaused = false;
					m_Minimized = false;
					TheGraphics::Instance()->GetD3DObj()->OnResize();
				}

				// Restoring from maximized state?
				else if( m_Maximized )
				{
					m_AppPaused = false;
					m_Maximized = false;
					TheGraphics::Instance()->GetD3DObj()->OnResize();
				}
				else if( m_Resizing )
				{
					// If user is dragging the resize bars, we do not resize 
					// the buffers here because as the user continuously 
					// drags the resize bars, a stream of WM_SIZE messages are
					// sent to the window, and it would be pointless (and slow)
					// to resize for each WM_SIZE message received from dragging
					// the resize bars.  So instead, we reset after the user is 
					// done resizing the window and releases the resize bars, which 
					// sends a WM_EXITSIZEMOVE message.
				}
				else // API call such as SetWindowPos or mSwapChain->SetFullscreenState.
				{
					TheGraphics::Instance()->GetD3DObj()->OnResize();
				}
			}
		}
		return 0;

		// WM_EXITSIZEMOVE is sent when the user grabs the resize bars.
	case WM_ENTERSIZEMOVE:
		//m_AppPaused = true;
		m_Resizing  = true;
		//mTimer.stop();
		return 0;

		// WM_EXITSIZEMOVE is sent when the user releases the resize bars.
		// Here we reset everything based on the new window dimensions.
	case WM_EXITSIZEMOVE:
		m_AppPaused = false;
		m_Resizing  = false;
		//mTimer.start();
		TheGraphics::Instance()->GetD3DObj()->OnResize();
		return 0;

		// WM_DESTROY is sent when the window is being destroyed.
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

		// The WM_MENUCHAR message is sent when a menu is active and the user presses 
		// a key that does not correspond to any mnemonic or accelerator key. 
	case WM_MENUCHAR:
		// Don't beep when we alt-enter.
		return MAKELRESULT(0, MNC_CLOSE);

		// Catch this message so to prevent the window from becoming too small.
	case WM_GETMINMAXINFO:
		((MINMAXINFO*)lparam)->ptMinTrackSize.x = 200;
		((MINMAXINFO*)lparam)->ptMinTrackSize.y = 200; 
		return 0;

		// Any other messages send to the default message handler as our application won't make use of them.
	default:
		{
			return DefWindowProc(hwnd, umsg, wparam, lparam);
		}
	}
}

void App::InitWindow(int& ScreenWidth, int& ScreenHeight)
{
	WNDCLASS wc;

	// Get an external pointer to this object.
	g_App = this;

	// Get the instance of this application.
	m_hAppInst = GetModuleHandle(NULL);

	// Setup the windows class with default settings.
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = m_hAppInst;
	wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
	//wc.hIconSm = wc.hIcon;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = "D3DWndClassName";
	//wc.cbSize = sizeof(WNDCLASSEX);

	// Register the window class.
	if(!RegisterClass(&wc))
	{
		//MessageBox(0, "RegisterClass FAILED", 0,0);
		ReportError("RegisterClass FAILED");
		PostQuitMessage(0);
	}

	//// Determine the resolution of the clients desktop screen.
	//ScreenWidth  = GetSystemMetrics(SM_CXSCREEN);
	//ScreenHeight = GetSystemMetrics(SM_CYSCREEN);

	//// Setup the screen settings depending on whether it is running in full screen or in windowed mode.
	//if(FULL_SCREEN)
	//{
	//	// If full screen set the screen to maximum size of the users desktop and 32bit.
	//	memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
	//	dmScreenSettings.dmSize       = sizeof(dmScreenSettings);
	//	dmScreenSettings.dmPelsWidth  = (unsigned long)screenWidth;
	//	dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight;
	//	dmScreenSettings.dmBitsPerPel = 32;			
	//	dmScreenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

	//	// Change the display settings to full screen.
	//	ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);

	//	// Set the position of the window to the top left corner.
	//	posX = posY = 0;
	//}
	//
	//else
	//{
	//	// If windowed then set it to 800x600 resolution.
	//	screenWidth  = 800;
	//	screenHeight = 600;

	//	// Place the window in the middle of the screen.
	//	posX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth)  / 2;
	//	posY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2;
	//}

	// Create the window with the screen settings and get the handle to it.

	// Compute window rectangle dimensions based on requested client area dimensions.
	RECT R = { 0, 0, ScreenWidth, ScreenHeight };
	AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
	int width  = R.right - R.left;
	int height = R.bottom - R.top;

	m_hMainWnd = CreateWindow("D3DWndClassName", 
		m_MainWndCaption.c_str(), 
		WS_OVERLAPPEDWINDOW | WS_POPUP,
		CW_USEDEFAULT, 
		CW_USEDEFAULT, 
		width, 
		height, 
		NULL, 
		NULL, 
		m_hAppInst, 
		this);

	if (!m_hMainWnd)
	{
		//MessageBox(0, "Create Window Failed", 0,0);
		ReportError("Create Window Failed");
		PostQuitMessage(0);
	}

	// Bring the window up on the screen and set it as main focus.
	ShowWindow(m_hMainWnd, SW_SHOW);
	SetForegroundWindow(m_hMainWnd);
	SetFocus(m_hMainWnd);

	//Dont Hide the mouse cursor.
	ShowCursor(true);

	return;
}

void App::ShutdownWindows()
{
	// Show the mouse cursor.
	ShowCursor(true);

	// Fix the display settings if leaving full screen mode.
	if(FULL_SCREEN)
	{
		ChangeDisplaySettings(NULL, 0);
	}

	// Remove the window.
	DestroyWindow(m_hMainWnd);
	m_hMainWnd = NULL;

	// Remove the application instance.
	UnregisterClass(m_MainWndCaption.c_str(), m_hAppInst);
	m_hAppInst = NULL;

	// Release the pointer to this class.
	g_App = NULL;

	return;
}
